package com.yth.utils;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用于生成脚本代码的工具类，支持头部和正文部分的代码构建。
 *
 * @author yutianhong
 * @version 1.0
 * @since 2025/4/27 10:42
 */
@SuppressWarnings("UnusedReturnValue")  // method chaining
public class ScriptWriter {
    private static final String INDENT = "    ";
    private static final String LINE_BREAK = "\n";

    private final Set<String> importSet;
    private final Section headSection;
    private final Section bodySection;
    private final Section activeSection;

    public ScriptWriter() {
        headSection = new Section();
        bodySection = new Section();
        importSet = new HashSet<>();
        activeSection = bodySection;
    }

    private ScriptWriter(ScriptWriter parent, SectionType sectionType) {
        headSection = parent.headSection;
        bodySection = parent.bodySection;
        importSet = parent.importSet;
        activeSection = (sectionType == SectionType.HEAD) ? headSection : bodySection;
    }

    /**
     * 生成并返回完整的脚本字符串
     */
    public String build() {
        String importString = importSet.stream()
                .map(e -> new StringBuilder("import ").append(e).append(";\n"))
                .collect(Collectors.joining());
        return (importString + LINE_BREAK + headSection.getContent() + bodySection.getContent()).trim();
    }

    /**
     * 清空所有内容
     */
    public ScriptWriter clear() {
        headSection.clear();
        bodySection.clear();
        importSet.clear();
        return this;
    }

    /**
     * 切换到头部部分进行代码编写
     */
    public ScriptWriter header() {
        return new ScriptWriter(this, SectionType.HEAD);
    }

    /**
     * 切换回正文部分进行代码编写
     */
    public ScriptWriter body() {
        return new ScriptWriter(this, SectionType.BODY);
    }

    /**
     * 添加导入类名或包名到脚本中
     */
    public ScriptWriter addImport(String importName) {
        validateParamsNonNull(importName, "importName");
        String trim = importName.trim();
        if (!trim.isEmpty()) {
            importSet.add(trim);
        }
        return this;
    }

    /**
     * 增加当前代码段的缩进级别
     */
    public ScriptWriter indent() {
        activeSection.increaseIndentLevel();
        return this;
    }

    /**
     * 减少当前代码段的缩进级别
     */
    public ScriptWriter unindent() {
        activeSection.decreaseIndentLevel();
        return this;
    }

    /**
     * 在当前部分追加一行代码
     */
    public ScriptWriter appendLine(String codeLine) {
        activeSection.appendLine(codeLine);
        return this;
    }

    /**
     * 在当前部分追加一个空行
     */
    public ScriptWriter appendBlankLine() {
        appendLine("");
        return this;
    }

    /**
     * 在当前部分追加一行注释
     */
    public ScriptWriter appendCommentLine(String comment) {
        if (comment == null) {
            return this;
        }

        String trimmed = comment.trim();
        if (!trimmed.isEmpty()) {
            appendLine("/** " + trimmed + " **/");
        }
        return this;
    }

    /**
     * 追加一个变量赋值语句到当前部分
     */
    public ScriptWriter appendAssignment(String variable, String value) {
        validateParamsNonNull(variable, "variable");
        validateParamsNonNull(value, "value");

        return appendLine(variable + " = " + value + ";");
    }

    /**
     * 追加一个返回语句到当前部分
     */
    public ScriptWriter appendReturnStatement(String expression) {
        validateParamsNonNull(expression, "expression");

        return appendLine("return " + expression + ";");
    }

    /**
     * 开始一个for循环
     *
     * @see ScriptWriter#endForLoop()
     */
    public ScriptWriter startForLoop(String indexVar, String limitExp) {
        validateParamsNonNull(indexVar, "indexVar");
        validateParamsNonNull(limitExp, "limitExp");

        String loopHeader = String.format(
                "for (%s = 0; %s < %s; %s++) {",
                indexVar, indexVar, limitExp, indexVar
        );
        appendLine(loopHeader);
        indent();
        return this;
    }

    /**
     * 结束当前for循环
     *
     * @see ScriptWriter#startForLoop(String, String)
     */
    public ScriptWriter endForLoop() {
        unindent();
        return appendLine("}");
    }

    /**
     * 开始一个if条件块
     *
     * @see ScriptWriter#endIf()
     */
    public ScriptWriter startIf(String conditionExp) {
        validateParamsNonNull(conditionExp, "conditionExp");
        String ifHeader = String.format(
                "if (%s) {", conditionExp
        );
        appendLine(ifHeader);
        indent();
        return this;
    }

    /**
     * 开始else块
     *
     * @see ScriptWriter#startIf(String)
     */
    public ScriptWriter startElse() {
        unindent();
        appendLine("} else {");
        indent();
        return this;
    }

    /**
     * 开始else if条件块
     *
     * @see ScriptWriter#startIf(String)
     */
    public ScriptWriter startElseIf(String conditionExp) {
        validateParamsNonNull(conditionExp, "conditionExp");
        unindent();
        String elseHeader = String.format("} else if (%s) {", conditionExp);
        appendLine(elseHeader);
        indent();
        return this;
    }

    /**
     * 结束if条件块
     *
     * @see ScriptWriter#startIf(String)
     */
    public ScriptWriter endIf() {
        unindent();
        return appendLine("}");
    }

    /**
     * 开始一个函数定义
     *
     * @see ScriptWriter#endFunction()
     */
    public ScriptWriter startFunction(String functionName) {
        validateParamsNonNull(functionName, "functionName");

        appendLine("function " + functionName + "() {");
        indent();
        return this;
    }

    /**
     * 结束当前函数定义
     *
     * @see ScriptWriter#startFunction(String)
     */
    public ScriptWriter endFunction() {
        unindent();
        return appendLine("}");
    }

    private void validateParamsNonNull(Object param, String paramName) {
        Objects.requireNonNull(param, () -> paramName + " must not be null");
    }

    private static class Section {
        private final StringBuilder codeBuffer = new StringBuilder();
        private int indentLevel = 0;

        String getContent() {
            return codeBuffer.toString();
        }

        void clear() {
            codeBuffer.setLength(0);
            indentLevel = 0;
        }

        void increaseIndentLevel() {
            indentLevel++;
        }

        void decreaseIndentLevel() {
            indentLevel--;
        }

        void appendLine(String script) {
            if (script == null) {
                return;
            }
            script = script.trim();
            if (!script.isEmpty()) {
                appendIndent().append(script).append(LINE_BREAK);
            } else {
                codeBuffer.append(LINE_BREAK);
            }
        }

        private StringBuilder appendIndent() {
            for (int i = 0; i < indentLevel; i++) {
                codeBuffer.append(INDENT);
            }
            return codeBuffer;
        }
    }

    private enum SectionType {
        HEAD, BODY,
    }
}
